#include "linux/of.h"
#include "linux/printk.h"
#include "linux/hardirq.h"
#include "linux/slab.h"
#include "linux/types.h"
#include "asm/ptrace.h"
#include "linux/errno.h"
#include "linux/irq.h"
#include "linux/irqdesc.h"
#include "linux/radix-tree.h"

#define NR_IRQS 160
int nr_irqs = NR_IRQS;

static struct radix_tree_root irq_desc_tree = {
	.rnode = 
	{
		.next = &irq_desc_tree.rnode,
		.prev = &irq_desc_tree.rnode,
	},
};

static DECLARE_BITMAP(allocated_irqs, NR_IRQS);
struct irq_desc irq_desc[NR_IRQS] = {0};

static void ack_bad(struct irq_data *data)
{
	// struct irq_desc *desc = irq_data_to_desc(data);

	// print_irq_desc(data->irq, desc);
	// ack_bad_irq(data->irq);
}

void ack_bad_irq(int irq)
{
	printk("Unexpected IRQ %d\n", irq);
}


static unsigned int noop_ret(struct irq_data *data)
{
	return 0;
}

static void noop(struct irq_data *data) {}

struct irq_chip no_irq_chip = {
	.name = "none",
	.irq_startup = noop_ret,
	.irq_shutdown = noop,
	.irq_enable = noop,
	.irq_disable = noop,
	.irq_ack = ack_bad,
};

static void irq_insert_desc(unsigned int irq, struct irq_desc *desc)
{
	radix_tree_insert(&irq_desc_tree, irq, desc);
}

struct irq_desc *irq_to_desc(unsigned int irq)
{
	return radix_tree_lookup(&irq_desc_tree, irq);
}

static void delete_irq_desc(unsigned int irq)
{
	radix_tree_delete(&irq_desc_tree, irq);
}

struct irq_desc *__irq_get_desc_lock(unsigned int irq, unsigned long *flags, int bus,	unsigned int check)
{
	struct irq_desc *desc = irq_to_desc(irq);

	if (desc)
	{
		//raw_spin_lock_irqsave(&desc->lock, *flags);
	}
	return desc;
}

struct irq_desc *irq_get_desc_lock(unsigned int irq, unsigned long *flags, unsigned int check)
{
	return __irq_get_desc_lock(irq, flags, 0, check);
}

void __irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags, int bus)
{
	//raw_spin_unlock_irqrestore(&desc->lock, flags);
}

void irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags)
{
	__irq_put_desc_unlock(desc, flags, 0);
}

struct irq_desc* irq_get_desc_buslock(unsigned int irq, unsigned long *flags, unsigned int check)
{
	return __irq_get_desc_lock(irq, flags, 1, check);
}

void irq_put_desc_busunlock(struct irq_desc *desc, unsigned long flags)
{
	__irq_put_desc_unlock(desc, flags, 1);
}

static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node, void *owner)
{
	//int cpu;

	desc->irq_data.irq = irq;
	desc->irq_data.chip = &no_irq_chip;
	desc->irq_data.chip_data = NULL;
	desc->irq_data.handler_data = NULL;
	// desc->irq_data.msi_desc = NULL;
	// irq_settings_clr_and_set(desc, ~0, _IRQ_DEFAULT_INIT_FLAGS);
	// irqd_set(&desc->irq_data, IRQD_IRQ_DISABLED);
	desc->handle_irq = handle_bad_irq;
	desc->depth = 1;
	desc->irq_count = 0;
	desc->irqs_unhandled = 0;
	desc->name = NULL;
	// desc->owner = owner;
	// for_each_possible_cpu(cpu)
	//*per_cpu_ptr(desc->kstat_irqs, cpu) = 0;
	// desc_smp_init(desc, node);
}

static struct irq_desc *alloc_desc(int irq, int node, void *owner)
{
	struct irq_desc *desc;

	desc = (struct irq_desc *)kzalloc(sizeof (struct irq_desc), GFP_KERNEL);
	if (!desc)
		return NULL;

	//raw_spin_lock_init(&desc->lock);

	desc_set_defaults(irq, desc, node, owner);

	return desc;
}

static void free_desc(unsigned int irq)
{
	struct irq_desc *desc = irq_to_desc(irq);

	kfree(desc);
}

struct irq_desc *desc_irq_18;

static int alloc_descs(unsigned int start, unsigned int cnt, int node, void *owner)
{
	struct irq_desc *desc;
	int i;

	for (i = 0; i < cnt; i++)
	{
		desc = alloc_desc(start + i, node, owner);
		if (!desc)
			goto err;
		//mutex_lock(&sparse_irq_lock);
		if (start + i == 18)
		{
			desc_irq_18 = desc;
		}
		irq_insert_desc(start + i, desc);
		//mutex_unlock(&sparse_irq_lock);
	}
	return start;

err:
	for (i--; i >= 0; i--)
		free_desc(start + i);

	// mutex_lock(&sparse_irq_lock);
	bitmap_clear(allocated_irqs, start, cnt);
	// mutex_unlock(&sparse_irq_lock);
	return -ENOMEM;
}

int __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node, void *owner)
{
	int start, ret;

	if (!cnt)
		return -EINVAL;

	if (irq >= 0)
	{
		if (from > irq)
			return -EINVAL;
		from = irq;
	}

	// mutex_lock(&sparse_irq_lock);

	start = bitmap_find_next_zero_area(allocated_irqs, NR_IRQS, from, cnt);
	ret = -EEXIST;
	if (irq >= 0 && start != irq)
		goto err;

	if (start + cnt > nr_irqs)
	{
		//ret = irq_expand_nr_irqs(start + cnt);
		//if (ret)
			//goto err;
	}

	bitmap_set(allocated_irqs, start, cnt);
	// mutex_unlock(&sparse_irq_lock);
	return alloc_descs(start, cnt, node, owner);

err:
	//mutex_unlock(&sparse_irq_lock);
	return ret;
}

void irq_free_descs(unsigned int from, unsigned int cnt)
{
	int i;

	if (from >= nr_irqs || (from + cnt) > nr_irqs)
		return;

	for (i = 0; i < cnt; i++)
		free_desc(from + i);

	//mutex_lock(&sparse_irq_lock);
	bitmap_clear(allocated_irqs, from, cnt);
	//mutex_unlock(&sparse_irq_lock);
}

void irq_free_desc(unsigned int irq)
{
	irq_free_descs(irq, 1);
}

static void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
	desc->handle_irq(irq, desc);
}

int generic_handle_irq(unsigned int irq)
{
	struct irq_desc *desc = irq_to_desc(irq);

	if (!desc)
		return -EINVAL;
	generic_handle_irq_desc(irq, desc);
	return 0;
}

int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq, int lookup, struct pt_regs *regs)
{
	//struct pt_regs *old_regs = set_irq_regs(regs);
	unsigned int irq = hwirq;
	int ret = 0;

	irq_enter();

	if (lookup)
		irq = irq_find_mapping(domain, hwirq);

	/*
	 * Some hardware gives randomly wrong interrupts.  Rather
	 * than crashing, do something sensible.
	 */
	if (!irq || irq >= nr_irqs)
	{
		ack_bad_irq(irq);
		ret = -EINVAL;
	}
	else
	{
		generic_handle_irq(irq);
	}

	irq_exit();
	//set_irq_regs(old_regs);
	return ret;
}

int handle_domain_irq(struct irq_domain *domain, unsigned int hwirq, struct pt_regs *regs)
{
	return __handle_domain_irq(domain, hwirq, 1, regs);
}